/** sol09.6.c
 ** ------------------------------------------------------------
	A version of controlflow.c that supports the else part of the
	if..then control structure is
	sol09.6.c.

	This file can be linked with

	 cc smsh2.c splitline.c execute.c process2.c sol09.6.c \
	 	builtin.c varlib.c -o sol6

	to produce a shell that allows else in if structures.  If you replace
	splitline.c with sol09.4.c, you can add multiple commands on one line,
	and if you replace builtin.c with sol09.5.c, you can add cd and exit.
	Pretty modular design, eh?

 ** ------------------------------------------------------------
 **
 **
 *   controlflow.c with else included
 *
 *
 * "if" processing is done with two state variables
 *    if_state and if_result
 */
#include	<stdio.h>
#include	<string.h>
#include	"smsh.h"

enum states   { NEUTRAL, WANT_THEN, THEN_BLOCK, ELSE_BLOCK };
enum results  { SUCCESS, FAIL };

static int if_state  = NEUTRAL;
static int if_result = SUCCESS;
static int last_stat = 0;

int	syn_err(char *);

int ok_to_execute()
/*
 * purpose: determine the shell should execute a command
 * returns: 1 for yes, 0 for no
 * details: if in THEN_BLOCK and if_result was SUCCESS then yes
 *          if in THEN_BLOCK and if_result was FAIL    then no
 *          if in WANT_THEN  then syntax error (sh is different)
 */
{
	int	rv = 1;		/* default is positive */

	if ( if_state == WANT_THEN ){
		syn_err("then expected");
		rv = 0;
	}
	else if ( if_state == THEN_BLOCK && if_result == SUCCESS )
		rv = 1;
	else if ( if_state == THEN_BLOCK && if_result == FAIL )
		rv = 0;
	else if ( if_state == ELSE_BLOCK && if_result == FAIL )
		rv = 1;
	else if ( if_state == ELSE_BLOCK && if_result == SUCCESS )
		rv = 0;
	return rv;
}

int is_control_command(char *s)
/*
 * purpose: boolean to report if the command is a shell control command
 * returns: 0 or 1
 */
{
    return ( strcmp(s,"if")==0 || strcmp(s,"then")==0 ||
	     strcmp(s,"fi")==0 || strcmp(s,"else")==0  );
}


int do_control_command(char **args)
/*
 * purpose: Process "if", "then", "fi" - change state or detect error
 *    also: process "else"
 * returns: 0 if ok, -1 for syntax error
 *   notes: I would have put returns all over the place, Barry says "no"
 */
{
	char	*cmd = args[0];
	int	rv = -1;

	if( strcmp(cmd,"if")==0 ){
		if ( if_state != NEUTRAL )
			rv = syn_err("if unexpected");
		else {
			last_stat = process(args+1);
			if_result = (last_stat == 0 ? SUCCESS : FAIL );
			if_state = WANT_THEN;
			rv = 0;
		}
	}
	else if ( strcmp(cmd,"then")==0 ){
		if ( if_state != WANT_THEN )
			rv = syn_err("then unexpected");
		else {
			if_state = THEN_BLOCK;
			rv = 0;
		}
	}
	else if ( strcmp(cmd,"fi")==0 ){
		if ( if_state != THEN_BLOCK && if_state != ELSE_BLOCK )
			rv = syn_err("fi unexpected");
		else {
			if_state = NEUTRAL;
			rv = 0;
		}
	}
	else if ( strcmp(cmd,"else")==0 ){
		if ( if_state != THEN_BLOCK )
			rv = syn_err("else unexpected");
		else {
			if_state = ELSE_BLOCK;
			rv = 0;
		}
	}
	else 
		fatal("internal error processing:", cmd, 2);
	return rv;
}

int syn_err(char *msg)
/* purpose: handles syntax errors in control structures
 * details: resets state to NEUTRAL
 * returns: -1 in interactive mode. Should call fatal in scripts
 */
{
	if_state = NEUTRAL;
	fprintf(stderr,"syntax error: %s\n", msg);
	return -1;
}
